#pragma once
#include <Rz/Object.h>
#include <Rz/IEquatable.h>
#include <Rz/ITypeWrapper.h>

namespace Rz { namespace Collections {

template <typename T>
class TConstIterator :
	public Object,
	public IEquatable<TConstIterator<T>>,
	public ITypeWrapper<T>
{
	public:
		RZ_RTTI(Rz::Collections, TConstIterator);
		RZ_RTTI_IMPLEMENTS(Object, IEquatable<TConstIterator<T>>, ITypeWrapper<T>);
		RZ_RTTI_TEMPLATES(NonObjectType<T>);
		RZ_RTTI_END;
                
		RZ_TYPEWRAPPER_IMPLEMENT(T);
                
		RZ_INLINE TConstIterator() { }
		RZ_INLINE TConstIterator(const TConstIterator<T>& i) : _ptr(i._ptr) { }
		RZ_INLINE TConstIterator(Pointer p) : _ptr(p) { }

		RZ_INLINE ConstReference operator * () const { return (*this->_ptr); }
		RZ_INLINE ConstPointer operator -> () const { return reinterpret_cast<Pointer>(**this); }

		RZ_INLINE TConstIterator& operator ++ () { ++this->_ptr; return *this; }
		RZ_INLINE TConstIterator  operator ++ (int) { TConstIterator<T> _tmp = *this; ++*this; return _tmp; }

		RZ_INLINE TConstIterator& operator -- () { --this->_ptr; return *this; }
		RZ_INLINE TConstIterator  operator -- (int) { TConstIterator<T> _tmp = *this; --*this; return _tmp; }

		RZ_INLINE TConstIterator& operator += (PointerDifferenceType offset) { _ptr += offset; return *this; }
		RZ_INLINE TConstIterator operator + (PointerDifferenceType offset) const { TConstIterator<T> _tmp = *this; return (_tmp += offset); }

		RZ_INLINE TConstIterator& operator -= (PointerDifferenceType offset) { return (*this += -offset); }
		RZ_INLINE TConstIterator operator - (PointerDifferenceType offset) const { TConstIterator<T> _tmp = *this; return (_tmp -= offset); }

		RZ_INLINE PointerDifferenceType operator - (const TConstIterator& r) const { return (this->_ptr - r._ptr); }

		RZ_INLINE Reference operator [] (PointerDifferenceType index) const { return (*(*this + index)); }

		RZ_INLINE bool operator < (const TConstIterator& r) const { return (this->_ptr < r._ptr); }
		RZ_INLINE bool operator > (const TConstIterator& r) const { return (r < *this); }

		RZ_INLINE bool operator <= (const TConstIterator& r) const { return (!(r < *this)); }
		RZ_INLINE bool operator >= (const TConstIterator& r) const { return (!(*this < r)); }

		RZ_INLINE ConstPointer GetPointer() const { return _ptr; }

		// IEquatable
		RZ_INLINE bool Equals(const TConstIterator<T>& r) const override { return (this->_ptr == r._ptr); }
		RZ_INLINE bool operator == (const TConstIterator<T>& r) const override { return  Equals(r); }
		RZ_INLINE bool operator != (const TConstIterator<T>& r) const override { return !Equals(r); }

	protected:
		Pointer _ptr;
};

template <typename T>
class TIterator :
	public TConstIterator<T>,
	public IEquatable<TIterator<T>>
{
	public:
		RZ_RTTI(Rz::Collections, TIterator);
		RZ_RTTI_IMPLEMENTS(TConstIterator<T>, IEquatable<TIterator<T>>);
		RZ_RTTI_TEMPLATES(NonObjectType<T>);
		RZ_RTTI_END;
                
		RZ_TYPEWRAPPER_IMPLEMENT(T);

		typedef TConstIterator<T> Base;

		RZ_INLINE TIterator() { }
		RZ_INLINE TIterator(const TIterator<T>& i) : TIterator<T>(i._ptr) { }
		RZ_INLINE TIterator(Pointer p) : TConstIterator<T>(p) { }

		RZ_INLINE Reference operator * () { return ((Reference)**(Base*)this); }
		RZ_INLINE Pointer operator -> () { return reinterpret_cast<Pointer>(**this); }

		RZ_INLINE TIterator& operator ++ () { ++*(Base*)this; return *this; }
		RZ_INLINE TIterator  operator ++ (int) { TIterator<T> _tmp = *this; ++*this; return _tmp; }
 
		RZ_INLINE TIterator& operator -- () { --*(Base*)this; return *this; }
		RZ_INLINE TIterator  operator -- (int) { TIterator<T> _tmp = *this; --*this; return _tmp; }

		RZ_INLINE TIterator& operator += (PointerDifferenceType offset) { *(Base*)this += offset; return *this; }
		RZ_INLINE TIterator operator + (PointerDifferenceType offset) const { TIterator<T> _tmp = *this; return (_tmp += offset); }

		RZ_INLINE TIterator& operator -= (PointerDifferenceType offset) { return (*this += -offset); }
		RZ_INLINE TIterator operator - (PointerDifferenceType offset) const { TIterator<T> _tmp = *this; return (_tmp -= offset); }

		RZ_INLINE PointerDifferenceType operator - (const Base& r) const { return (*(Base*)this - r); }

		RZ_INLINE Reference operator [] (PointerDifferenceType index) const { return (*(*this + index)); }

		RZ_INLINE Pointer GetPointer() { return TConstIterator<T>::_ptr; }

		// IEquatable
		RZ_INLINE bool Equals(const TIterator<T>& r) const override { return (this->_ptr == r._ptr); }
		RZ_INLINE bool operator == (const TIterator<T>& r) const override { return  Equals(r); }
		RZ_INLINE bool operator != (const TIterator<T>& r) const override { return !Equals(r); }
};

template <typename T, typename TIt>
class TConstIteratorAdapter :
	public Object,
	public IEquatable<TConstIteratorAdapter<T, TIt>>,
	public ITypeWrapper<T>
{
	public:
		RZ_RTTI(Rz::Collections, TConstIteratorAdapter);
		RZ_RTTI_IMPLEMENTS(Object, IEquatable<TConstIteratorAdapter<T, TIt>>, ITypeWrapper<T>);
		RZ_RTTI_TEMPLATES(NonObjectType<T>, NonObjectType<TIt>);
		RZ_RTTI_END;
                
		RZ_TYPEWRAPPER_IMPLEMENT(T);
                
		RZ_INLINE TConstIteratorAdapter() { }
 		RZ_INLINE TConstIteratorAdapter(const TConstIteratorAdapter<T, TIt>& i) : _iterator(i._iterator) { }
		RZ_INLINE TConstIteratorAdapter(TIt it) : _iterator(it) { }

		RZ_INLINE ConstReference operator * () const { return (*this->_iterator); }
		RZ_INLINE ConstPointer operator -> () const { return reinterpret_cast<Pointer>(**this); }

		RZ_INLINE TConstIteratorAdapter& operator ++ () { ++this->_iterator; return *this; }
		RZ_INLINE TConstIteratorAdapter  operator ++ (int) { TConstIteratorAdapter<T, TIt> _tmp = *this; ++*this; return _tmp; }

		RZ_INLINE TConstIteratorAdapter& operator -- () { --this->_ptr; return *this; }
		RZ_INLINE TConstIteratorAdapter  operator -- (int) { TConstIteratorAdapter<T, TIt> _tmp = *this; --*this; return _tmp; }

		RZ_INLINE TConstIteratorAdapter& operator += (PointerDifferenceType offset) { _iterator += offset; return *this; }
		RZ_INLINE TConstIteratorAdapter operator + (PointerDifferenceType offset) const { TConstIteratorAdapter<T, TIt> _tmp = *this; return (_tmp += offset); }

		RZ_INLINE TConstIteratorAdapter& operator -= (PointerDifferenceType offset) { return (*this += -offset); }
		RZ_INLINE TConstIteratorAdapter operator - (PointerDifferenceType offset) const { TConstIteratorAdapter<T, TIt> _tmp = *this; return (_tmp -= offset); }

		RZ_INLINE PointerDifferenceType operator - (const TConstIteratorAdapter& r) const { return (this->_iterator - r._iterator); }

		RZ_INLINE Reference operator [] (PointerDifferenceType index) const { return (*(*this + index)); }

		RZ_INLINE bool operator < (const TConstIteratorAdapter& r) const { return (this->_iterator < r._iterator); }
		RZ_INLINE bool operator > (const TConstIteratorAdapter& r) const { return (r < *this); }

		RZ_INLINE bool operator <= (const TConstIteratorAdapter& r) const { return (!(r < *this)); }
		RZ_INLINE bool operator >= (const TConstIteratorAdapter& r) const { return (!(*this < r)); }

		RZ_INLINE ConstPointer GetPointer() const { return *_iterator; }

		// IEquatable
		RZ_INLINE bool Equals(const TConstIteratorAdapter<T, TIt>& r) const override { return (this->_iterator == r._iterator); }
		RZ_INLINE bool operator == (const TConstIteratorAdapter<T, TIt>& r) const override { return  Equals(r); }
		RZ_INLINE bool operator != (const TConstIteratorAdapter<T, TIt>& r) const override { return !Equals(r); }

	protected:
		TIt _iterator;
};

template <typename T, typename TIt>
class TIteratorAdapter :
	public Object,
	public IEquatable<TIteratorAdapter<T, TIt>>,
	public ITypeWrapper<T>
{
	public:
		RZ_RTTI(Rz::Collections, TIteratorAdapter);
		RZ_RTTI_IMPLEMENTS(Object, IEquatable<TIteratorAdapter<T, TIt>>, ITypeWrapper<T>);
		RZ_RTTI_TEMPLATES(NonObjectType<T>, NonObjectType<TIt>);
		RZ_RTTI_END;
                
		RZ_TYPEWRAPPER_IMPLEMENT(T);
                
		RZ_INLINE TIteratorAdapter() { }
 		RZ_INLINE TIteratorAdapter(const TIteratorAdapter<T, TIt>& i) : _iterator(i._iterator) { }
		RZ_INLINE TIteratorAdapter(TIt it) : _iterator(it) { }

		RZ_INLINE Reference operator*() { return (*this->_iterator); }
		RZ_INLINE Pointer operator->() { return reinterpret_cast<Pointer>(**this); }

		RZ_INLINE TIteratorAdapter& operator ++ () { ++this->_iterator; return *this; }
		RZ_INLINE TIteratorAdapter  operator ++ (int) { TIteratorAdapter<T, TIt> _tmp = *this; ++*this; return _tmp; }

		RZ_INLINE TIteratorAdapter& operator -- () { --this->_ptr; return *this; }
		RZ_INLINE TIteratorAdapter  operator -- (int) { TIteratorAdapter<T, TIt> _tmp = *this; --*this; return _tmp; }

		RZ_INLINE TIteratorAdapter& operator += (PointerDifferenceType offset) { _iterator += offset; return *this; }
		RZ_INLINE TIteratorAdapter operator + (PointerDifferenceType offset) const { TIteratorAdapter<T, TIt> _tmp = *this; return (_tmp += offset); }

		RZ_INLINE TIteratorAdapter& operator -= (PointerDifferenceType offset) { return (*this += -offset); }
		RZ_INLINE TIteratorAdapter operator - (PointerDifferenceType offset) const { TIteratorAdapter<T, TIt> _tmp = *this; return (_tmp -= offset); }

		RZ_INLINE PointerDifferenceType operator - (const TIteratorAdapter& r) const { return (this->_iterator - r._iterator); }

		RZ_INLINE Reference operator [] (PointerDifferenceType index) const { return (*(*this + index)); }

		RZ_INLINE bool operator < (const TIteratorAdapter& r) const { return (this->_iterator < r._iterator); }
		RZ_INLINE bool operator > (const TIteratorAdapter& r) const { return (r < *this); }

		RZ_INLINE bool operator <= (const TIteratorAdapter& r) const { return (!(r < *this)); }
		RZ_INLINE bool operator >= (const TIteratorAdapter& r) const { return (!(*this < r)); }

		RZ_INLINE ConstPointer GetPointer() const { return *_iterator; }

		// IEquatable
		RZ_INLINE bool Equals(const TIteratorAdapter<T, TIt>& r) const override { return (this->_iterator == r._iterator); }
		RZ_INLINE bool operator == (const TIteratorAdapter<T, TIt>& r) const override { return  Equals(r); }
		RZ_INLINE bool operator != (const TIteratorAdapter<T, TIt>& r) const override { return !Equals(r); }

	protected:
		TIt _iterator;
};

} }
